import { Block } from "./Block";
import { Blockchain } from "./Blockchain";
import net from 'net'

type Message = {
    type: 'blockchain_request' | 'blockchain_response' | 'peers_request' | 'peers_response' | 'keep_alive',
    data: any
}

export class P2PServer {
    blockchain: Blockchain;
    port: number; // P2P 服务器端口
    host: string; // P2P 服务器主机
    peers: Map<string, net.Socket> = new Map(); // P2P 服务器节点列表
    seedPeers: string[]; // 种子节点列表

    constructor (blockchain: Blockchain, port: number = 12315, host: string = 'localhost', seedPeers: string[] = ['localhost:12315']) {
        this.blockchain = blockchain;
        this.port = port;
        this.host = host;
        this.seedPeers = seedPeers;
    }

    // 启动 P2P 服务器
    listen () {
        const server = net.createServer((socket) => {
            this.handleConnection(socket);
        })

        server.listen(this.port, this.host, () => {
            console.log(`Server listening on ${this.host}:${this.port}`)

            // 连接到种子节点
            this.connectToSeedPeers();
        })

        // 保持程序活跃
        this.keepAlive();
    }

    // 处理新连接,每个新连接都会向对方节点请求最新区块链和节点列表
    handleConnection (socket: net.Socket) {
        console.debug('New connection fron ', socket.remoteAddress, ':', socket.remotePort);

        // 设置 socket 事件处理程序
        this.setupSocketEventHandles(socket);
        this.requestBlockchain(socket);
        this.requestPeers(socket);
    }

    // 连接种子节点
    connectToSeedPeers () {
        this.seedPeers.forEach((peer) => {
            const [host, port] = peer.split(':');
            this.connectToPeer(host, parseInt(port));
        })
    }

    // 设置 socket 事件处理程序
    setupSocketEventHandles (socket: net.Socket) {
        socket.on('data', (data) => this.handleSocketData(socket, data))
        socket.on('close', () => this.handleSocketClose(socket))
        socket.on('error', (error) => this.handleSocketError(socket, error))
    }

    // 处理 socket 数据
    handleSocketData (socket: net.Socket, data: Buffer) {
        const messages = data.toString().split('\n').filter((s) => s.length > 0);
        messages.forEach((messageStr) => {
            const message: Message = JSON.parse(messageStr);
            console.debug('Received message: ', message);
            this.handleMessage(socket, message);
        })
    }

    // 处理 socket 错误
    handleSocketError (socket: net.Socket, error: Error) {
        console.error('Socket error:', error.message);
        this.removePeer(socket);
    }

    // 处理 socket 关闭
    handleSocketClose (socket: net.Socket) {
        console.debug('Socket closed');
        this.removePeer(socket);
    }

    // 从节点列表中移除节点
    removePeer (socket: net.Socket) {
        const key = `${socket.remoteAddress}:${socket.remotePort}`;
        this.peers.delete(key);
    }

    handleMessage(socket: net.Socket, message: Message) {
        switch (message.type) {
            case 'blockchain_request':
                console.debug('Received blockchain request.');
                this.sendBlockchain(socket);
                break;
            case 'blockchain_response':
                console.debug('Received blockchain response.');
                this.handleBlockchainResponse(message.data);
                break;
            case 'peers_request':
                console.debug('Received peers request.');
                this.sendPeers(socket, message.data);
                break;
            case 'peers_response':
                console.debug('Received peers response.');
                this.handlePeersResponse(message.data);
            case 'keep_alive':
                console.debug('Received keep alive message.');
                break;
            default:
                console.error('Unknown message type: ', message.type)
                break;
        }
    }

    // 请求区块链数据
    requestBlockchain (socket: net.Socket) {
        const message: Message = {
            type: 'blockchain_request',
            data: null
        }
        this.sendMessage(socket, message);
    }

    // 发生区块链
    sendBlockchain(socket: net.Socket) {
        const message: Message = {
            type: 'blockchain_response',
            data: this.blockchain.chain
        }
        this.sendMessage(socket, message)
    }

    // 处理区块链响应
    handleBlockchainResponse(data: any) {
        console.debug('Received blockchain response: ', data);
        const newChain = data.map((data: any) => {
            const block = new Block(
                data.index,
                data.timestamp,
                data.data,
                data.previousHash
            )
            block.hash = data.hash;
            block.nonce = data.nonce;
            return block;
        });
        if (!this.blockchain.isChainValid(newChain)) {
            console.error('Received blockchain is invalid');
            return
        }
        if (this.blockchain.height < newChain.length) {
            this.blockchain.chain = newChain;
            this.blockchain.saveChainToFile();
        } else {
            console.debug('Received blockchain is not longer than current blockchain.')
        }
    }

    // 请求节点列表
    requestPeers (socket: net.Socket) {
        const message: Message = {
            type: 'peers_request',
            data: {
                host: this.host,
                port: this.port
            }
        }
        this.sendMessage(socket, message);
    }

    // 发送节点
    sendPeers (socket: net.Socket, data: any) {
        this.peers.set(`${data.host}:${data.port}`, socket)
        const message: Message = {
            type: 'peers_response',
            data: Array.from(this.peers.keys())
        }
        this.sendMessage(socket, message)
    }

    // 处理节点响应
    handlePeersResponse(data: any) {
        console.debug('Received peers response: ', data);
        data.forEach((peer: string) => {
            if (!this.peers.has(peer) && this.seedPeers.indexOf(peer) === -1) {
                console.debug('Connecting to new peer: ', peer);
                const [host, port] = peer.split(':');
                this.connectToPeer(host, parseInt(port));
            }
        })
    }

    // 连接节点
    connectToPeer(host: string, port: number) {
        if (this.peers.has(`${host}:${port}`)) {
            console.debug('Already connected to peer: ', `${host}:${port}`);
            return;
        }
        if (host === this.host && this.port === port) {
            console.debug('Cannot connect to self');
            return;
        }
        const socket = net.connect(port, host, () => {
            this.handleConnection(socket);
        })

        this.setupSocketEventHandles(socket);
        this.peers.set(`${host}:${port}`, socket);
    }

    // 保持程序运行
    keepAlive() {
        setInterval(() => {
            this.broadcast({
                type: 'keep_alive',
                data: null
            })
        }, 1000 * 60 * 5)

        // 打印日志
        setInterval(() => {
            console.debug('Peers: ', Array.from(this.peers.keys()));
            console.debug('Blockchain height: ', this.blockchain.height);
        }, 1000 * 5)
    }

    // 广播消息
    broadcast(message: Message) {
        this.peers.forEach(socket => {
            this.sendMessage(socket, message)
        })
    }

    // 发送消息
    sendMessage (socket: net.Socket, message: Message) {
        const messageStr = JSON.stringify(message) + '\n';
        socket.write(messageStr);
    }
}